/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package no.ffi.tpm;

import iaik.tc.apps.jtt.common.CommonSettings;
import iaik.tc.apps.jtt.ek.ReadEkCert;
import iaik.tc.tss.api.exceptions.common.TcTssException;
import iaik.tc.tss.api.structs.common.TcBlobData;
import iaik.tc.tss.api.structs.tsp.TcTssVersion;
import iaik.tc.tss.api.tspi.TcIContext;
import iaik.tc.tss.api.tspi.TcITpm;
import iaik.tc.tss.api.tspi.TcTssAbstractFactory;
import iaik.tc.tss.impl.java.tsp.TcTpm;
import iaik.tc.utils.logging.Log;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author Federico Mancinie <fma at ffi.no>
 */
public class TPMUtils {
    
    public static final int UINT32=4;
    public static final int UINT16=2;
    public static final int BYTE=1;
    
      /**
     * Decodes an integer from a short array when  byte order is big endian
     * @param elements Array to decode
     * @return The int represented by the input short array
     */
    public static int decodeUINT16(short[] elements) {

        int tmp = 0;

        tmp |= elements[0] & 0x00ff;
        tmp <<= 8;
        tmp |= elements[1] & 0x00ff;

        return tmp;
    }

    /**
     * Decodes a long from a short array when  byte order is big endian
     * @param elements Array to decode
     * @return The long represented by the input short array
     */
    public static long decodeUINT32(short[] elements) {

        long tmp = 0;

        tmp |= (byte) elements[0] & 0x00ff;
        tmp <<= 8;
        tmp |= (byte) elements[1] & 0x00ff;
        tmp <<= 8;
        tmp |= (byte) elements[2] & 0x00ff;
        tmp <<= 8;
        tmp |= elements[3] & 0x00ff;

        tmp |= elements[0] & 0x00ff;

        return tmp;
    }

    /**
     * Decodes a byte into a short 
     * @param b The byte to decode
     * @return The corresponding short value
     */
    public static short decodeByte(byte b) {
        short retVal = (short) (b & 0xff);

        return retVal;
    }

    /**
     * Extract a subarray from a given byte array
     * @param data The original byte array
     * @param index The index where the subarray starts
     * @param numElements The length of the subarray
     * @return 
     */    
    public static byte[] getRange(byte[] data, int index, int numElements) {

        byte[] retVal = new byte[numElements];

        System.arraycopy(data, index, retVal, 0, numElements);

        return retVal;
    }

    /**
     * The same as decodeByte....
     * @param b
     * @return 
     */
    private static short unsignedByteToShort(byte b) {
        return (short) (b & 0xff);
    }

    /**
     * This method converts a byte array into a short array.
     */
    public static short[] byteArrayToShortArray(byte[] input) {
        short[] retVal = new short[input.length];
        for (int i = 0; i < input.length; i++) {
            retVal[i] = unsignedByteToShort(input[i]);
        }
        return retVal;
    }

    /**
	 * This method converts a byte representation of a boolean value into the Java boolean type
	 */
	public static boolean byteToBoolean(final byte value)
	{
		if (value != 0) {
			return true;
		} else {
			return false;
		}
    }

    /**
     * ***********************************************************************************************
     * Initializes the object using the provided long value. Note that only
     * positive long arguments are accepted. Otherwise an
     * IllegalArgumentException is thrown. This is the way, the UINT32 (unsigned
     * 32 bit integer) specified in the TCG specs, is handled.
     *
     * @param input Single int the object is initialized with.
     * @return this Pointer of the object
     *
     *
     * @throws IllegalArgumentException If negative arguments are supplied, this
     * exception is thrown.
     */
    public static byte[] initUINT32(long input) {

        byte[] data = new byte[4];


        data[0] = (byte) (input & 0x000000ff); // LSB is stored in lowest address (LSB first)
        input >>= 8;
        data[1] = (byte) (input & 0x000000ff);
        input >>= 8;
        data[2] = (byte) (input & 0x000000ff);
        input >>= 8;
        data[3] = (byte) (input & 0x000000ff);


        return data;
    }
    public static RSAPublicKey pubTpmKeyToJava(byte[] TPMpubKey)
	{
            // BigInteger requires a leading sign-byte
		byte[] pubKey=new byte[TPMpubKey.length+1];
                pubKey[0]=((byte) 0);
                System.arraycopy(TPMpubKey, 0, pubKey, 1, TPMpubKey.length);
  
		RSAPublicKeySpec pubEkSpec = new RSAPublicKeySpec(new BigInteger(pubKey),
				new BigInteger("65537"));

		RSAPublicKey pubKeyJava = null;

		try {
                
                    pubKeyJava = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(pubEkSpec);
                } catch (InvalidKeySpecException ex) {
                    Logger.getLogger(TPMUtils.class.getName()).log(Level.SEVERE, null, ex);
                }  catch (NoSuchAlgorithmException e) {
			// can be ignored since startup checks were successful
		}

		return pubKeyJava;
	}
    
     /**
     * Method used to extract the EK fro the TPM
     * @param ownersecret
     * @return 
     */
    public static TcBlobData getEK(TcBlobData ownersecret) {

        TcBlobData temp = null;
        try {
            TcTssAbstractFactory tssFactory = CommonSettings.getTssFactory();
            TcIContext context = tssFactory.newContextObject();

            context.connect();

            try {

                TcITpm tpm = context.getTpmObject();
                TcTssVersion version = ((TcTpm) tpm).getRealTpmVersion();

                if (CommonSettings.onIFXStack_ || version.equalsMinMaj(TcTssVersion.TPM_V1_2)) {
                    temp = ReadEkCert.getEkCertAlternative(ownersecret);
                } else {
                    temp = ReadEkCert.getEkCert();
                }

            } catch (Exception e) //a lot of things can go wrong with NV-RAM!
            {
                Log.info("Could not extract EK Certificate from TPM.");
                temp = null;
            }



        } catch (TcTssException ex) {
            Logger.getLogger(TPMUtils.class.getName()).log(Level.SEVERE, null, ex);
        }
        return temp;
    }

}
